using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using viBook.Common;
using viBook.Helpers;
using viBook.Models;

namespace viBook.Repositories
{
    public class BookRepository : IBookRepository
    {
        private readonly IAuthorRepository _authorRepository;
        private readonly IBookCommentRepository _bookCommentRepository;
        private readonly IBookContentTagRepository _bookContentTagRepository;
        private readonly ICategoryRepository _categoryRepository;
        private readonly IChapterRepository _chapterRepository;
        private readonly IContentTagRepository _contentTagRepository;
        private readonly viBookContext _context;

        public BookRepository(IAuthorRepository authorRepositoryRepository,
                              IChapterRepository chapterRepository,
                              ICategoryRepository categoryRepository,
                              IContentTagRepository contentTagRepository,
                              IBookContentTagRepository bookContentTagRepository,
                              IBookCommentRepository bookCommentRepository)
        {
            _context = new viBookContext();
            _authorRepository = authorRepositoryRepository;
            _chapterRepository = chapterRepository;
            _categoryRepository = categoryRepository;
            _contentTagRepository = contentTagRepository;
            _contentTagRepository = contentTagRepository;
            _bookContentTagRepository = bookContentTagRepository;
            _bookCommentRepository = bookCommentRepository;
        }

        #region IBookRepository Members

        public IQueryable<Book> All
        {
            get { return _context.Books; }
        }

        public Book Find(int id)
        {
            Book book = _context.Books.Find(id);
            if (book != null)
            {
                book.AuthorName = _authorRepository.Find(book.AuthorId).AuthorName;
                book.Tag = string.Empty;
                var listcontentTags = _context.BookContentTags.Where(bct => bct.BookId == id).ToList();
                foreach (var bookContentTag in listcontentTags)
                {
                    var contentTag = _context.ContentTags.Find(bookContentTag.ContentTagId);
                    if (contentTag != null)
                        book.Tag += contentTag.ContentTagName+", ";
                }
                book.Chapters = new List<Chapter>();
                book.Chapters = _chapterRepository.GetAllChaptersByBookId(id);
            }
            return book;
        }


        public void InsertOrUpdate(Book book)
        {
            if (book.AuthorName != null)
                book.AuthorId = _authorRepository.FindAuthorId(book.AuthorName.Trim());
            else
                book.AuthorName = _authorRepository.Find(book.AuthorId).AuthorName;
            if (book.Description != null)
                book.Description = book.Description.Trim();
            if (book.Id == 0)
            {
                _context.Books.Add(book);
                Save();
            }
            else
            {
                if (!string.IsNullOrEmpty(book.ListSortedChapterId))
                {
                    string[] listChapterId = book.ListSortedChapterId.Split(new[] {';'},
                                                                            StringSplitOptions.RemoveEmptyEntries);
                    int chapterNumber = 0;
                    foreach (string chapterId in listChapterId)
                    {
                        chapterNumber++;
                        Chapter chapter = _chapterRepository.Find(int.Parse(chapterId));
                        chapter.ChapterNumber = chapterNumber;
                        _chapterRepository.InsertOrUpdate(chapter);
                        _chapterRepository.Save();
                    }
                }
                _context.Entry(book).State = EntityState.Modified;
                Save();

                if (!string.IsNullOrEmpty(book.Tag))
                    EditContentTag(book);
            }
            if (!string.IsNullOrEmpty(book.Tag))
                InsertContentTag(book);
        }

        public void Delete(int id)
        {
            _bookCommentRepository.DeleteAllCommentByBookId(id);
            _bookContentTagRepository.DeleteAllTagByBookId(id);
            _chapterRepository.DeleteChaptersInBook(id);
            Book book = _context.Books.Find(id);
            Helpers.Helpers.DeleteFile(book.CoverUrl);
            _context.Books.Remove(book);
        }

        public void Save()
        {
            _context.SaveChanges();
        }

        public DataFeedback Like(int id, int userId)
        {
            var feedback = new DataFeedback();
            BookLike existLike =
                _context.BookLikes.Where(b => b.UserId == userId).Where(l => l.BookId == id).FirstOrDefault();
            if (existLike == null)
            {
                _context.BookLikes.Add(new BookLike {BookId = id, UserId = userId, CreatedDate = DateTime.Now});
                Save();

                feedback.IsSuccess = true;
            }
            else
            {
                _context.BookLikes.Remove(existLike);
                Save();
                Book book = _context.Books.Find(id);
                book.NumberOfLikes--;
                InsertOrUpdate(book);
                feedback.IsSuccess = false;
                feedback.Message = Constant.ExistBookLike;
            }
            return feedback;
        }

        public IQueryable<Book> FindBookByCategoryId(int id)
        {
            return
                _context.Books.Where(c => c.CategoryId == id && c.IsBlocked==false).OrderByDescending(b => b.UploadedDate).Take(
                    Constant.MaximunBookPerPage);
        }

        public IQueryable<Book> GetMostPopularBook()
        {
            return _context.Books.Where(b=>b.IsBlocked==false).OrderByDescending(b => b.NumberOfViews).Take(Constant.MaximunPopBookPage);
        }

        public IEnumerable<Book> SearchBook(string searchContent)
        {
            List<Book> result =
                _context.Books.Where(
                    b =>
                    b.Title.ToLower().Contains(searchContent.ToLower()) && b.IsBlocked==false).ToList();
            foreach (Book book in result)
            {
                if (book == null) continue;
                book.NumberOfLikes = _context.BookLikes.Where(bl => bl.BookId == book.Id).ToList().Count();
                book.NumberOfComments = _context.BookComments.Where(bc => bc.BookId == book.Id).ToList().Count();
                book.Uploader = _context.Users.Where(u => u.Id == book.UploadedBy).FirstOrDefault();
                if (book.Uploader != null)
                {
                    book.UploaderName = book.Uploader.Username;
                    book.UploaderAvartar = book.Uploader.AvatarUrl;
                }
            }
            return result;
        }

        public IQueryable<Book> FetchCommentsByPage(int pageNumber)
        {
            int skipItem = (pageNumber - 1)*Constant.MaximunItemsToPaging;
            return
                _context.Books.Where(b=>b.IsBlocked==false).OrderByDescending(p => p.NumberOfViews).Skip(skipItem).Take(Constant.MaximunItemsToPaging);
        }

        public DataFeedback ReportBook(int id, int userId)
        {
            var feedback = new DataFeedback();
            ReportOnBook existLike =
                _context.ReportOnBooks.FirstOrDefault(b => b.UserId == userId && b.BookId == id);
            if (existLike == null)
            {
                _context.ReportOnBooks.Add(new ReportOnBook {BookId = id, UserId = userId, ReportedDate = DateTime.Now});
                Save();

                Book book = _context.Books.Find(id);
                book.IsReported = true;
                InsertOrUpdate(book);
                feedback.IsSuccess = true;
            }
            else
            {
                feedback.IsSuccess = false;
                feedback.Message = Constant.ErrorMessage;
            }
            return feedback;
        }

        public Book FindWithAddedView(int id, int userId)
        {
            Book book = _context.Books.Find(id);
            if (book != null)
            {
                book.NumberOfViews++;
                InsertOrUpdate(book);

                book.AuthorName = _authorRepository.Find(book.AuthorId).AuthorName;
                book.CategoryName = _categoryRepository.Find(book.CategoryId).CategoryName;

                List<BookComment> totalComments = _context.BookComments.Where(c => c.BookId == book.Id && c.IsBlocked==false).ToList();
                book.Uploader = _context.Users.Find(book.UploadedBy);

              
                IEnumerable<BookComment> comments =
                    totalComments.OrderByDescending(c => c.CreatedDate).Take(Constant.MaximunCommentPerChapter);
                int noOfComments = totalComments.Count();

                foreach (BookComment bookComment in comments)
                {
                    BookComment comment = bookComment;
                    bookComment.IsLiked =
                        _context.BookCommentLikes.Any(bcl => bcl.CommentId == comment.Id && bcl.UserId == userId);
                    bookComment.IsReported =
                         _context.ReportOnBookComments.Any(
                             rocc => rocc.BookCommentId == bookComment.Id && rocc.UserId == userId);
                    bookComment.IsAuthorized = bookComment.UserId == userId;
                }

                book.Comments = comments;
                book.NumberOfComments = noOfComments;
                book.NumberOfLikes = _context.BookLikes.Where(bl => bl.BookId == book.Id).ToList().Count();
                book.Chapters = new List<Chapter>();
                book.Chapters = _chapterRepository.GetAllChaptersByBookId(id);
                book.ContentTags = new List<ContentTag>();
                book.ContentTags = _contentTagRepository.GetAllContentTagByBookId(book.Id);
                book.IsLiked = _context.BookLikes.Any(l => l.BookId == book.Id && l.UserId == userId);
                book.IsReported = _context.ReportOnBooks.Any(rob => rob.BookId == book.Id && rob.UserId == userId);
            }
            return book;
        }

        public DataFeedback UnLike(int id, int userId)
        {
            BookLike foundLike = _context.BookLikes.FirstOrDefault(l => l.BookId == id && l.UserId == userId);
            if (foundLike != null)
            {
                _context.BookLikes.Remove(foundLike);
                Save();
                return new DataFeedback {IsSuccess = true};
            }
            return new DataFeedback {IsSuccess = false};
        }

        public IEnumerable<Book> GetUserBookByBookId(int id)
        {
            Book book = _context.Books.Where(b => b.Id == id).FirstOrDefault();
            if (book != null)
            {
                return
                    _context.Books.Where(b => b.UploadedBy == book.UploadedBy && b.IsBlocked==false).OrderByDescending(b => b.NumberOfViews).
                        Take(Constant.MaximunUserBook).
                        ToList();
            }
            return null;
        }

        public IEnumerable<Book> GetAuthorBookByBookId(int id)
        {
            Book book = _context.Books.Where(b => b.Id == id).FirstOrDefault();
            if (book != null)
            {
                return
                    _context.Books.Where(b => b.AuthorId == book.AuthorId && b.IsBlocked==false).OrderByDescending(b => b.NumberOfViews).Take(
                        Constant.MaximunUserBook).
                        ToList();
            }
            return null;
        }

        #endregion

        public void InsertContentTag(Book book)
        {
            IEnumerable<string> listContentTags =
                book.Tag.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Distinct();
            foreach (string contenTag in listContentTags)
            {
                if (contenTag == null || string.IsNullOrEmpty(contenTag.Trim())) continue;
                int contentId = _contentTagRepository.FindTagsByContent(contenTag.Trim());
                if (contentId == 0)
                {
                    var newContentTag = new ContentTag {ContentTagName = contenTag.Trim()};
                    _contentTagRepository.InsertNew(newContentTag);
                    _contentTagRepository.Save();
                    contentId = newContentTag.Id;
                }
                _bookContentTagRepository.InsertOrUpdate(new BookContentTags
                                                             {BookId = book.Id, ContentTagId = contentId});
                _bookContentTagRepository.Save();
            }
        }

        private void EditContentTag(Book book)
        {
            _bookContentTagRepository.DeleteAllTagByBookId(book.Id);
        }
    }

    public interface IBookRepository
    {
        IQueryable<Book> All { get; }
        Book Find(int id);
        void InsertOrUpdate(Book book);
        void Delete(int id);
        void Save();
        DataFeedback Like(int id, int userId);
        IQueryable<Book> FindBookByCategoryId(int id);
        IQueryable<Book> GetMostPopularBook();
        IEnumerable<Book> SearchBook(string searchContent);
        IQueryable<Book> FetchCommentsByPage(int pageNumber);
        DataFeedback ReportBook(int id, int userId);
        Book FindWithAddedView(int id, int userId);
        DataFeedback UnLike(int id, int i);
        IEnumerable<Book> GetUserBookByBookId(int id);
        IEnumerable<Book> GetAuthorBookByBookId(int id);
    }
}